/***************************************************************************
                             qgsprocessingalgorithmdialogbase.cpp
                             ------------------------------------
    Date                 : November 2017
    Copyright            : (C) 2017 Nyall Dawson
    Email                : nyall dot dawson at gmail dot com
 ***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "qgsprocessingalgorithmdialogbase.h"
#include "qgssettings.h"
#include "qgshelp.h"
#include "qgsmessagebar.h"
#include "qgsgui.h"
#include "processing/qgsprocessingalgorithm.h"
#include "processing/qgsprocessingprovider.h"
#include "qgstaskmanager.h"
#include "processing/qgsprocessingalgrunnertask.h"
#include "qgsstringutils.h"
#include "qgsapplication.h"
#include "qgspanelwidget.h"
#include <QToolButton>
#include <QDesktopServices>
#include <QScrollBar>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QMimeData>


///@cond NOT_STABLE

QgsProcessingAlgorithmDialogFeedback::QgsProcessingAlgorithmDialogFeedback()
  : QgsProcessingFeedback( false )
{}

void QgsProcessingAlgorithmDialogFeedback::setProgressText( const QString &text )
{
  QgsProcessingFeedback::setProgressText( text );
  emit progressTextChanged( text );
}

void QgsProcessingAlgorithmDialogFeedback::reportError( const QString &error, bool fatalError )
{
  QgsProcessingFeedback::reportError( error, fatalError );
  emit errorReported( error, fatalError );
}

void QgsProcessingAlgorithmDialogFeedback::pushWarning( const QString &warning )
{
  QgsProcessingFeedback::pushWarning( warning );
  emit warningPushed( warning );
}

void QgsProcessingAlgorithmDialogFeedback::pushInfo( const QString &info )
{
  QgsProcessingFeedback::pushInfo( info );
  emit infoPushed( info );
}

void QgsProcessingAlgorithmDialogFeedback::pushCommandInfo( const QString &info )
{
  QgsProcessingFeedback::pushCommandInfo( info );
  emit commandInfoPushed( info );
}

void QgsProcessingAlgorithmDialogFeedback::pushDebugInfo( const QString &info )
{
  QgsProcessingFeedback::pushDebugInfo( info );
  emit debugInfoPushed( info );
}

void QgsProcessingAlgorithmDialogFeedback::pushConsoleInfo( const QString &info )
{
  QgsProcessingFeedback::pushConsoleInfo( info );
  emit consoleInfoPushed( info );
}

//
// QgsProcessingAlgorithmDialogBase
//

QgsProcessingAlgorithmDialogBase::QgsProcessingAlgorithmDialogBase( QWidget *parent, Qt::WindowFlags flags )
  : QDialog( parent, flags )
{
  setupUi( this );

  //don't collapse parameters panel
  splitter->setCollapsible( 0, false );

  // add collapse button to splitter
  QSplitterHandle *splitterHandle = splitter->handle( 1 );
  QVBoxLayout *handleLayout = new QVBoxLayout();
  handleLayout->setContentsMargins( 0, 0, 0, 0 );
  mButtonCollapse = new QToolButton( splitterHandle );
  mButtonCollapse->setAutoRaise( true );
  mButtonCollapse->setFixedSize( 12, 12 );
  mButtonCollapse->setCursor( Qt::ArrowCursor );
  handleLayout->addWidget( mButtonCollapse );
  handleLayout->addStretch();
  splitterHandle->setLayout( handleLayout );

  QgsGui::enableAutoGeometryRestore( this );

  QgsSettings settings;
  splitter->restoreState( settings.value( QStringLiteral( "/Processing/dialogBaseSplitter" ), QByteArray() ).toByteArray() );
  mSplitterState = splitter->saveState();
  splitterChanged( 0, 0 );

  // Rename OK button to Run
  mButtonRun = mButtonBox->button( QDialogButtonBox::Ok );
  mButtonRun->setText( tr( "Run" ) );

  // Rename Yes button. Yes is used to ensure same position of Run and Change Parameters with respect to Close button.
  mButtonChangeParameters = mButtonBox->button( QDialogButtonBox::Yes );
  mButtonChangeParameters->setText( tr( "Change Parameters" ) );

  buttonCancel->setEnabled( false );
  mButtonClose = mButtonBox->button( QDialogButtonBox::Close );

  connect( mButtonRun, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::runAlgorithm );
  connect( mButtonChangeParameters, &QPushButton::clicked, this, &QgsProcessingAlgorithmDialogBase::showParameters );
  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsProcessingAlgorithmDialogBase::closeClicked );
  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsProcessingAlgorithmDialogBase::openHelp );
  connect( mButtonCollapse, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::toggleCollapsed );
  connect( splitter, &QSplitter::splitterMoved, this, &QgsProcessingAlgorithmDialogBase::splitterChanged );

  connect( mButtonSaveLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::saveLog );
  connect( mButtonCopyLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::copyLogToClipboard );
  connect( mButtonClearLog, &QToolButton::clicked, this, &QgsProcessingAlgorithmDialogBase::clearLog );

  connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged );

  mMessageBar = new QgsMessageBar();
  mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
  verticalLayout->insertWidget( 0,  mMessageBar );

  connect( QgsApplication::taskManager(), &QgsTaskManager::taskTriggered, this, &QgsProcessingAlgorithmDialogBase::taskTriggered );
}

QgsProcessingAlgorithmDialogBase::~QgsProcessingAlgorithmDialogBase() = default;

void QgsProcessingAlgorithmDialogBase::setAlgorithm( QgsProcessingAlgorithm *algorithm )
{
  mAlgorithm.reset( algorithm );
  QString title;
  if ( ( QgsGui::higFlags() & QgsGui::HigDialogTitleIsTitleCase ) && !( algorithm->flags() & QgsProcessingAlgorithm::FlagDisplayNameIsLiteral ) )
  {
    title = QgsStringUtils::capitalize( mAlgorithm->displayName(), QgsStringUtils::TitleCase );
  }
  else
  {
    title = mAlgorithm->displayName();
  }
  setWindowTitle( title );

  QString algHelp = formatHelp( algorithm );
  if ( algHelp.isEmpty() )
    textShortHelp->hide();
  else
  {
    textShortHelp->document()->setDefaultStyleSheet( QStringLiteral( ".summary { margin-left: 10px; margin-right: 10px; }\n"
        "h2 { color: #555555; padding-bottom: 15px; }\n"
        "a { text - decoration: none; color: #3498db; font-weight: bold; }\n"
        "p { color: #666666; }\n"
        "b { color: #333333; }\n"
        "dl dd { margin - bottom: 5px; }" ) );
    textShortHelp->setHtml( algHelp );
    connect( textShortHelp, &QTextBrowser::anchorClicked, this, &QgsProcessingAlgorithmDialogBase::linkClicked );
  }

  if ( algorithm->helpUrl().isEmpty() && algorithm->provider()->helpId().isEmpty() )
  {
    mButtonBox->removeButton( mButtonBox->button( QDialogButtonBox::Help ) );
  }

  const QString warning = algorithm->provider()->warningMessage();
  if ( !warning.isEmpty() )
  {
    mMessageBar->pushMessage( warning, Qgis::Warning );
  }
}

QgsProcessingAlgorithm *QgsProcessingAlgorithmDialogBase::algorithm()
{
  return mAlgorithm.get();
}

void QgsProcessingAlgorithmDialogBase::setMainWidget( QgsPanelWidget *widget )
{
  if ( mMainWidget )
  {
    mMainWidget->deleteLater();
  }

  mPanelStack->setMainPanel( widget );
  widget->setDockMode( true );

  mMainWidget = widget;
  connect( mMainWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
}

QgsPanelWidget *QgsProcessingAlgorithmDialogBase::mainWidget()
{
  return mMainWidget;
}

void QgsProcessingAlgorithmDialogBase::saveLogToFile( const QString &path, const LogFormat format )
{
  QFile logFile( path );
  if ( !logFile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
  {
    return;
  }
  QTextStream fout( &logFile );

  switch ( format )
  {
    case FormatPlainText:
      fout << txtLog->toPlainText();
      break;

    case FormatHtml:
      fout << txtLog->toHtml();
      break;
  }
}

QgsProcessingFeedback *QgsProcessingAlgorithmDialogBase::createFeedback()
{
  auto feedback = qgis::make_unique< QgsProcessingAlgorithmDialogFeedback >();
  connect( feedback.get(), &QgsProcessingFeedback::progressChanged, this, &QgsProcessingAlgorithmDialogBase::setPercentage );
  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::commandInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushCommandInfo );
  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::consoleInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushConsoleInfo );
  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::debugInfoPushed, this, &QgsProcessingAlgorithmDialogBase::pushDebugInfo );
  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::errorReported, this, &QgsProcessingAlgorithmDialogBase::reportError );
  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::warningPushed, this, &QgsProcessingAlgorithmDialogBase::pushWarning );
  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::infoPushed, this, &QgsProcessingAlgorithmDialogBase::pushInfo );
  connect( feedback.get(), &QgsProcessingAlgorithmDialogFeedback::progressTextChanged, this, &QgsProcessingAlgorithmDialogBase::setProgressText );
  connect( buttonCancel, &QPushButton::clicked, feedback.get(), &QgsProcessingFeedback::cancel );
  return feedback.release();
}

QDialogButtonBox *QgsProcessingAlgorithmDialogBase::buttonBox()
{
  return mButtonBox;
}

QTabWidget *QgsProcessingAlgorithmDialogBase::tabWidget()
{
  return mTabWidget;
}

void QgsProcessingAlgorithmDialogBase::showLog()
{
  mTabWidget->setCurrentIndex( 1 );
}

void QgsProcessingAlgorithmDialogBase::showParameters()
{
  mTabWidget->setCurrentIndex( 0 );
}

QPushButton *QgsProcessingAlgorithmDialogBase::runButton()
{
  return mButtonRun;
}

QPushButton *QgsProcessingAlgorithmDialogBase::cancelButton()
{
  return buttonCancel;
}

QPushButton *QgsProcessingAlgorithmDialogBase::changeParametersButton()
{
  return mButtonChangeParameters;
}

void QgsProcessingAlgorithmDialogBase::clearProgress()
{
  progressBar->setMaximum( 0 );
}

void QgsProcessingAlgorithmDialogBase::setExecuted( bool executed )
{
  mExecuted = executed;
}

void QgsProcessingAlgorithmDialogBase::setExecutedAnyResult( bool executedAnyResult )
{
  mExecutedAnyResult = executedAnyResult;
}

void QgsProcessingAlgorithmDialogBase::setResults( const QVariantMap &results )
{
  mResults = results;
}

void QgsProcessingAlgorithmDialogBase::finished( bool, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
{

}

void QgsProcessingAlgorithmDialogBase::openHelp()
{
  QUrl algHelp = mAlgorithm->helpUrl();
  if ( algHelp.isEmpty() )
  {
    algHelp = QgsHelp::helpUrl( QStringLiteral( "processing_algs/%1/%2.html#%3" ).arg( mAlgorithm->provider()->helpId(), mAlgorithm->groupId(), QStringLiteral( "%1%2" ).arg( mAlgorithm->provider()->helpId() ).arg( mAlgorithm->name() ) ) );
  }

  if ( !algHelp.isEmpty() )
    QDesktopServices::openUrl( algHelp );
}

void QgsProcessingAlgorithmDialogBase::toggleCollapsed()
{
  if ( mHelpCollapsed )
  {
    splitter->restoreState( mSplitterState );
    mButtonCollapse->setArrowType( Qt::RightArrow );
  }
  else
  {
    mSplitterState = splitter->saveState();
    splitter->setSizes( QList<int>() << 1 << 0 );
    mButtonCollapse->setArrowType( Qt::LeftArrow );
  }
  mHelpCollapsed = !mHelpCollapsed;
}

void QgsProcessingAlgorithmDialogBase::splitterChanged( int, int )
{
  if ( splitter->sizes().at( 1 ) == 0 )
  {
    mHelpCollapsed = true;
    mButtonCollapse->setArrowType( Qt::LeftArrow );
  }
  else
  {
    mHelpCollapsed = false;
    mButtonCollapse->setArrowType( Qt::RightArrow );
  }
}

void QgsProcessingAlgorithmDialogBase::mTabWidget_currentChanged( int )
{
  updateRunButtonVisibility();
}

void QgsProcessingAlgorithmDialogBase::linkClicked( const QUrl &url )
{
  QDesktopServices::openUrl( url.toString() );
}

void QgsProcessingAlgorithmDialogBase::algExecuted( bool successful, const QVariantMap & )
{
  mAlgorithmTask = nullptr;

  if ( !successful )
  {
    // show dialog to display errors
    show();
    raise();
    setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
    activateWindow();
    showLog();
  }
  else
  {
    // delete dialog if closed
    if ( !isVisible() )
    {
      deleteLater();
    }
  }
}

void QgsProcessingAlgorithmDialogBase::taskTriggered( QgsTask *task )
{
  if ( task == mAlgorithmTask )
  {
    show();
    raise();
    setWindowState( ( windowState() & ~Qt::WindowMinimized ) | Qt::WindowActive );
    activateWindow();
    showLog();
  }
}

void QgsProcessingAlgorithmDialogBase::closeClicked()
{
  reject();
  close();
}

void QgsProcessingAlgorithmDialogBase::reportError( const QString &error, bool fatalError )
{
  setInfo( error, true );
  if ( fatalError )
    resetGui();
  showLog();
  processEvents();
}

void QgsProcessingAlgorithmDialogBase::pushWarning( const QString &warning )
{
  setInfo( warning, false, true, true );
  processEvents();
}

void QgsProcessingAlgorithmDialogBase::pushInfo( const QString &info )
{
  setInfo( info );
  processEvents();
}

void QgsProcessingAlgorithmDialogBase::pushCommandInfo( const QString &command )
{
  txtLog->append( QStringLiteral( "<code>%1<code>" ).arg( formatStringForLog( command.toHtmlEscaped() ) ) );
  scrollToBottomOfLog();
  processEvents();
}

void QgsProcessingAlgorithmDialogBase::pushDebugInfo( const QString &message )
{
  txtLog->append( QStringLiteral( "<span style=\"color:#777\">%1</span>" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
  scrollToBottomOfLog();
  processEvents();
}

void QgsProcessingAlgorithmDialogBase::pushConsoleInfo( const QString &info )
{
  txtLog->append( QStringLiteral( "<code style=\"color:#777\">%1</code>" ).arg( formatStringForLog( info.toHtmlEscaped() ) ) );
  scrollToBottomOfLog();
  processEvents();
}

QDialog *QgsProcessingAlgorithmDialogBase::createProgressDialog()
{
  QgsProcessingAlgorithmProgressDialog *dialog = new QgsProcessingAlgorithmProgressDialog( this );
  dialog->setWindowModality( Qt::ApplicationModal );
  dialog->setWindowTitle( windowTitle() );
  dialog->setGeometry( geometry() ); // match size/position to this dialog
  connect( progressBar, &QProgressBar::valueChanged, dialog->progressBar(), &QProgressBar::setValue );
  connect( dialog->cancelButton(), &QPushButton::clicked, buttonCancel, &QPushButton::click );
  dialog->logTextEdit()->setHtml( txtLog->toHtml() );
  connect( txtLog, &QTextEdit::textChanged, dialog, [this, dialog]()
  {
    dialog->logTextEdit()->setHtml( txtLog->toHtml() );
    QScrollBar *sb = dialog->logTextEdit()->verticalScrollBar();
    sb->setValue( sb->maximum() );
  } );
  return dialog;
}

void QgsProcessingAlgorithmDialogBase::clearLog()
{
  txtLog->clear();
}

void QgsProcessingAlgorithmDialogBase::saveLog()
{
  QgsSettings settings;
  QString lastUsedDir = settings.value( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QDir::homePath() ).toString();

  QString filter;
  const QString txtExt = tr( "Text files" ) + QStringLiteral( " (*.txt *.TXT)" );
  const QString htmlExt = tr( "HTML files" ) + QStringLiteral( " (*.html *.HTML)" );

  QString path = QFileDialog::getSaveFileName( this, tr( "Save Log to File" ), lastUsedDir, txtExt + ";;" + htmlExt, &filter );
  if ( path.isEmpty() )
  {
    return;
  }

  settings.setValue( QStringLiteral( "/Processing/lastUsedLogDirectory" ), QFileInfo( path ).path() );

  LogFormat format = FormatPlainText;
  if ( filter == htmlExt )
  {
    format = FormatHtml;
  }
  saveLogToFile( path, format );
}

void QgsProcessingAlgorithmDialogBase::copyLogToClipboard()
{
  QMimeData *m = new QMimeData();
  m->setText( txtLog->toPlainText() );
  m->setHtml( txtLog->toHtml() );
  QClipboard *cb = QApplication::clipboard();

#ifdef Q_OS_LINUX
  cb->setMimeData( m, QClipboard::Selection );
#endif
  cb->setMimeData( m, QClipboard::Clipboard );
}

void QgsProcessingAlgorithmDialogBase::closeEvent( QCloseEvent *e )
{
  if ( !mHelpCollapsed )
  {
    QgsSettings settings;
    settings.setValue( QStringLiteral( "/Processing/dialogBaseSplitter" ), splitter->saveState() );
  }

  QDialog::closeEvent( e );

  if ( !mAlgorithmTask )
  {
    // when running a background task, the dialog is kept around and deleted only when the task
    // completes. But if not running a task, we auto cleanup (later - gotta give callers a chance
    // to retrieve results and execution status).
    deleteLater();
  }
}

void QgsProcessingAlgorithmDialogBase::runAlgorithm()
{

}

void QgsProcessingAlgorithmDialogBase::setPercentage( double percent )
{
  // delay setting maximum progress value until we know algorithm reports progress
  if ( progressBar->maximum() == 0 )
    progressBar->setMaximum( 100 );
  progressBar->setValue( percent );
  processEvents();
}

void QgsProcessingAlgorithmDialogBase::setProgressText( const QString &text )
{
  lblProgress->setText( text );
  setInfo( text, false );
  scrollToBottomOfLog();
  processEvents();
}

QString QgsProcessingAlgorithmDialogBase::formatHelp( QgsProcessingAlgorithm *algorithm )
{
  QString text = algorithm->shortHelpString();
  if ( !text.isEmpty() )
  {
    QStringList paragraphs = text.split( '\n' );
    QString help;
    for ( const QString &paragraph : paragraphs )
    {
      help += QStringLiteral( "<p>%1</p>" ).arg( paragraph );
    }
    return QStringLiteral( "<h2>%1</h2>%2" ).arg( algorithm->displayName(), help );
  }
  else if ( !algorithm->shortDescription().isEmpty() )
  {
    return QStringLiteral( "<h2>%1</h2><p>%2</p>" ).arg( algorithm->displayName(), algorithm->shortDescription() );
  }
  else
    return QString();
}

void QgsProcessingAlgorithmDialogBase::processEvents()
{
  if ( mAlgorithmTask )
  {
    // no need to call this - the algorithm is running in a thread.
    // in fact, calling it causes a crash on Windows when the algorithm
    // is running in a background thread... unfortunately we need something
    // like this for non-threadable algorithms, otherwise there's no chance
    // for users to hit cancel or see progress updates...
    return;
  }

  // So that we get a chance of hitting the Abort button
#ifdef Q_OS_LINUX
  // One iteration is actually enough on Windows to get good interactivity
  // whereas on Linux we must allow for far more iterations.
  // For safety limit the number of iterations
  int nIters = 0;
  while ( ++nIters < 100 )
#endif
  {
    QCoreApplication::processEvents();
  }
}

void QgsProcessingAlgorithmDialogBase::scrollToBottomOfLog()
{
  QScrollBar *sb = txtLog->verticalScrollBar();
  sb->setValue( sb->maximum() );
}

void QgsProcessingAlgorithmDialogBase::resetGui()
{
  lblProgress->clear();
  progressBar->setMaximum( 100 );
  progressBar->setValue( 0 );
  mButtonRun->setEnabled( true );
  mButtonChangeParameters->setEnabled( true );
  mButtonClose->setEnabled( true );
  if ( mMainWidget )
  {
    mMainWidget->setEnabled( true );
  }
  updateRunButtonVisibility();
  resetAdditionalGui();
}

void QgsProcessingAlgorithmDialogBase::updateRunButtonVisibility()
{
  // Activate run button if current tab is Parameters
  bool runButtonVisible = mTabWidget->currentIndex() == 0;
  mButtonRun->setVisible( runButtonVisible );
  mButtonChangeParameters->setVisible( !runButtonVisible && mExecutedAnyResult && mButtonChangeParameters->isEnabled() );
}

void QgsProcessingAlgorithmDialogBase::resetAdditionalGui()
{

}

void QgsProcessingAlgorithmDialogBase::blockControlsWhileRunning()
{
  mButtonRun->setEnabled( false );
  mButtonChangeParameters->setEnabled( false );
  if ( mMainWidget )
  {
    mMainWidget->setEnabled( false );
  }
  blockAdditionalControlsWhileRunning();
}

void QgsProcessingAlgorithmDialogBase::blockAdditionalControlsWhileRunning()
{

}

QgsMessageBar *QgsProcessingAlgorithmDialogBase::messageBar()
{
  return mMessageBar;
}

void QgsProcessingAlgorithmDialogBase::hideShortHelp()
{
  textShortHelp->setVisible( false );
}

void QgsProcessingAlgorithmDialogBase::setCurrentTask( QgsProcessingAlgRunnerTask *task )
{
  mAlgorithmTask = task;
  connect( mAlgorithmTask, &QgsProcessingAlgRunnerTask::executed, this, &QgsProcessingAlgorithmDialogBase::algExecuted );
  QgsApplication::taskManager()->addTask( mAlgorithmTask );
}

QString QgsProcessingAlgorithmDialogBase::formatStringForLog( const QString &string )
{
  QString s = string;
  s.replace( '\n', QLatin1String( "<br>" ) );
  return s;
}

void QgsProcessingAlgorithmDialogBase::setInfo( const QString &message, bool isError, bool escapeHtml, bool isWarning )
{
  // note -- we have to wrap the message in a span block, or QTextEdit::append sometimes gets confused
  // and varies between treating it as a HTML string or a plain text string! (see https://github.com/qgis/QGIS/issues/37934)
  if ( isError || isWarning )
    txtLog->append( QStringLiteral( "<span style=\"color:%1\">%2</span>" ).arg( isError ? QStringLiteral( "red" ) : QStringLiteral( "#b85a20" ), escapeHtml ? formatStringForLog( message.toHtmlEscaped() ) : formatStringForLog( message ) ) );
  else if ( escapeHtml )
    txtLog->append( QStringLiteral( "<span>%1</span" ).arg( formatStringForLog( message.toHtmlEscaped() ) ) );
  else
    txtLog->append( QStringLiteral( "<span>%1</span>" ).arg( formatStringForLog( message ) ) );
  scrollToBottomOfLog();
  processEvents();
}

void QgsProcessingAlgorithmDialogBase::reject()
{
  if ( !mAlgorithmTask )
  {
    setAttribute( Qt::WA_DeleteOnClose );
  }
  QDialog::reject();
}

//
// QgsProcessingAlgorithmProgressDialog
//

QgsProcessingAlgorithmProgressDialog::QgsProcessingAlgorithmProgressDialog( QWidget *parent )
  : QDialog( parent )
{
  setupUi( this );
}

QProgressBar *QgsProcessingAlgorithmProgressDialog::progressBar()
{
  return mProgressBar;
}

QPushButton *QgsProcessingAlgorithmProgressDialog::cancelButton()
{
  return mButtonBox->button( QDialogButtonBox::Cancel );
}

QTextEdit *QgsProcessingAlgorithmProgressDialog::logTextEdit()
{
  return mTxtLog;
}

void QgsProcessingAlgorithmProgressDialog::reject()
{

}



///@endcond
